/*
* File: CFdEventSelect.cpp
* Copyright (c) 2010. Silviu Caragea <silviu@intelliproject.net>
* Created on September 9, 2010, 12:37 AM
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*  1.Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*  2.Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*  3.All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*       This product includes software developed by the University of
*       California, Berkeley and its contributors.
*  4.Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "CFdEventSelect.h"

#ifdef USE_SELECT

namespace litevent
{

int CFdEventSelect::reset()
{
    FD_ZERO(&(this->selectSetRead));
    FD_ZERO(&(this->selectSetWrite));
    FD_ZERO(&(this->selectSetError));
    this->selectMaxFd = -1;

    return 0;
}

int CFdEventSelect::deleteEvent(int fd_node, int fd )
{
    if (fd_node < 0)
        return 0;

    FD_CLR(fd, &(this->selectSetRead));
    FD_CLR(fd, &(this->selectSetWrite));
    FD_CLR(fd, &(this->selectSetError));

    return 1;
}

int CFdEventSelect::setEvent(int fd_node , int fd, int events)
{
    UNUSED(fd_node);

    if (events & FDEVENT_IN)
	FD_SET(fd, &(this->selectSetRead));
    else    
	FD_CLR(fd, &(this->selectSetRead));
    
    if (events & FDEVENT_OUT)    
	FD_SET(fd, &(this->selectSetWrite));
    else    
        FD_CLR(fd, &(this->selectSetWrite));

    if(events & FDEVENT_ERR)
        FD_SET(fd, &(this->selectSetError));
    else
        FD_CLR(fd, &(this->selectSetError));

    if (fd > this->selectMaxFd)
        this->selectMaxFd = fd;

    return fd;
}

int CFdEventSelect::poll(int timeoutMs)
{
    this->selectRead   =  this->selectSetRead;
    this->selectWrite  =  this->selectSetWrite;
    this->selectError  =  this->selectSetError;

    /*
        timeout = NULL => wait  until interrupted by a signal
        timeout = 0 tv => return immediatly     
    */
        
    if(timeoutMs < 0 )
        return select(this->selectMaxFd + 1, &(this->selectRead), &(this->selectWrite), &(this->selectError), NULL);

    struct timeval tv;
    tv.tv_sec =  timeoutMs / 1000;
    tv.tv_usec = (timeoutMs % 1000) * 1000;

    return select(this->selectMaxFd + 1, &(this->selectRead), &(this->selectWrite), &(this->selectError), &tv);
}

int CFdEventSelect::getEventRevent(size_t node)
{
    int revents = 0;

    if (FD_ISSET(node, &(this->selectRead)))    
    	revents |= FDEVENT_IN;
    
    if (FD_ISSET(node, &(this->selectWrite)))    
	revents |= FDEVENT_OUT;
    
    if (FD_ISSET(node, &(this->selectError)))    
	revents |= FDEVENT_ERR;
    
    return revents;
}


int CFdEventSelect::getEventFileDescriptor(size_t node)
{
    return node;
}

int CFdEventSelect::getEventNextFileDescriptor(int node)
{
    int i = (node < 0) ? 0 : node + 1;

    while (i < this->selectMaxFd+1)
    {
        if (FD_ISSET(i, &(this->selectRead)))
            return i;

	if (FD_ISSET(i, &(this->selectWrite)))
            return i;

	if (FD_ISSET(i, &(this->selectError)))
            return i;

        i++;
    }
  
    return -1;
}

}

#endif
